package com.dj.mingkong.portal.biz.service.music.impl;

import com.dj.mingkong.common.enums.MusicianStatusEnum;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.eo.BaseEo;
import com.dj.mingkong.common.infrastructure.eo.database.music.*;
import com.dj.mingkong.common.microservice.UserContext;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.common.util.SmartBeanCopy;
import com.dj.mingkong.portal.biz.constant.Constants;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianAddVo;
import com.dj.mingkong.portal.biz.controller.music.vo.request.MusicianUpdateVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicianReviewVo;
import com.dj.mingkong.portal.biz.controller.music.vo.response.MusicianVo;
import com.dj.mingkong.portal.biz.repository.database.music.*;
import com.dj.mingkong.portal.biz.service.music.MusicianService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zenglonghui
 * @date: 2023/3/27 16:06
 */
@Slf4j
@Service("musicianService")
public class MusicianServiceImpl implements MusicianService {
    @Resource
    private MusicianRepository musicianRepository;
    @Resource
    private MusicianReviewRepository musicianReviewRepository;
    @Resource
    private MusicianTypeRepository musicianTypeRepository;
    @Resource
    private MusicianStyleRepository musicianStyleRepository;
    @Resource
    private TypeRepository typeRepository;
    @Resource
    private StyleRepository styleRepository;

    @Override
    public List<MusicianVo> listMyMusician(Long userId) {
        final List<MusicianEo> musicianEos = musicianRepository.listByUserId(userId);
        final List<MusicianVo> musicianVos = SmartBeanCopy.copy(musicianEos, MusicianVo.class);
        if (CollectionUtils.isNotEmpty(musicianVos)) {
            musicianVos.forEach(this::buildVos);
        }
        return musicianVos;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insert(MusicianAddVo addVo) {
        if (musicianRepository.existByUserId(UserContext.getUserId())) {
            throw new ValidateException("您已绑定音乐人!");
        }
        checkMusicianVo(addVo);
        final MusicianEo musicianEo = SmartBeanCopy.copy(addVo, MusicianEo.class);
        musicianRepository.save(musicianEo);
        saveMusicianStyle(musicianEo.getId(), addVo.getStyleIdList());
        saveMusicianType(musicianEo.getId(), addVo.getTypeIdList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateById(MusicianUpdateVo updateVo) {
        final MusicianEo musicianEo = musicianRepository.getById(updateVo.getId());
        AssertUtils.isNotNull(musicianEo, "musicianEo invalid!");
        if (!musicianEo.getUserId().equals(UserContext.getUserId())) {
            throw new ValidateException("Modify without permission!");
        }
        if (MusicianStatusEnum.UNDER.equalsIntVal(musicianEo.getStatus())) {
            throw new ValidateException("under review!");
        }
        if (MusicianStatusEnum.PASS.equalsIntVal(musicianEo.getStatus())
                || MusicianStatusEnum.ACCUSE.equalsIntVal(musicianEo.getStatus())) {
            if (!musicianEo.getStageName().equals(updateVo.getStageName())) {
                throw new ValidateException("stageName not allow update!");
            }
            if (!musicianEo.getDemoUrl().equals(updateVo.getDemoUrl())) {
                throw new ValidateException("demo not allow update!");
            }
        }
        checkMusicianVo(updateVo);
        final MusicianEo eo = SmartBeanCopy.copy(updateVo, MusicianEo.class);
        musicianRepository.updateById(eo);
        saveMusicianStyle(musicianEo.getId(), updateVo.getStyleIdList());
        saveMusicianType(musicianEo.getId(), updateVo.getTypeIdList());
    }

   /* @Override
    public SearchHits<MusicianEsEo> searchLikeName(String name) {
        //设置查询分页
        final PageRequest pageRequest = PageRequest.of(0, CommonConstants.DEFAULT_PAGE_SIZE, WrapperUtil.buildEsSort(""));
        //构建查询条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.termQuery("status", MusicianStatusEnum.PASS.getIntVal()))
                .must(QueryBuilders.fuzzyQuery("stageName", name).fuzziness(Fuzziness.ONE));
        // 构建查询
        final NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .build();
        //执行查询
        return elasticsearchRestTemplate.search(nativeSearchQuery, MusicianEsEo.class);
    }

    @Override
    public SearchHits<MusicianEsEo> searchByVo(MusicianQueryEsVo queryVo) {
        //设置查询分页
        final PageRequest pageRequest = PageRequest.of(queryVo.getPage() - 1, queryVo.getPageSize(), WrapperUtil.buildEsSort(queryVo.getSort()));
        //构建查询条件
        final BoolQueryBuilder boolQueryBuilder = buildBoolQueryBuilder(queryVo);
        //设置高亮字段
        final HighlightBuilder highlightBuilder = buildHighlightBuilder();
        // 构建查询
        final NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withHighlightBuilder(highlightBuilder)
                .withPageable(pageRequest)
                .build();
        //执行查询
        return elasticsearchRestTemplate.search(nativeSearchQuery, MusicianEsEo.class);
    }

    private HighlightBuilder buildHighlightBuilder() {
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置标签前缀
        highlightBuilder.preTags("<font color='#00F7DE'>");
        //设置标签后缀
        highlightBuilder.postTags("</font>");
        //设置高亮字段
        highlightBuilder.field("stageName");
        return highlightBuilder;
    }

    private BoolQueryBuilder buildBoolQueryBuilder(MusicianQueryEsVo queryVo) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.termQuery("status", MusicianStatusEnum.PASS.getIntVal()))
                .must(QueryBuilders.fuzzyQuery("stageName", queryVo.getName()).fuzziness(Fuzziness.ONE));
        return boolQueryBuilder;
    }*/

    private void saveMusicianType(Long musicianId, List<Long> typeIdList) {
        final List<MusicianTypeEo> musicianTypeEos = musicianTypeRepository.listByMusicianId(musicianId);
        if (CollectionUtils.isNotEmpty(musicianTypeEos)) {
            final List<Long> addList = typeIdList.stream().filter(aLong ->
                    !musicianTypeEos.stream().map(MusicianTypeEo::getTypeId).toList().contains(aLong)).toList();
            saveMusicianType(addList, musicianId);
            final List<Long> delList = musicianTypeEos.stream().filter(typeEo ->
                    !typeIdList.contains(typeEo.getTypeId())).map(BaseEo::getId).toList();
            musicianTypeRepository.deleteByIds(delList);
        } else {
            saveMusicianType(typeIdList, musicianId);
        }
    }

    private void saveMusicianType(List<Long> typeIdList, Long musicianId) {
        if (CollectionUtils.isNotEmpty(typeIdList)) {
            final List<MusicianTypeEo> typeEos = typeIdList.stream().map(aLong ->
                    MusicianTypeEo.builder().typeId(aLong).musicianId(musicianId).build()).toList();
            musicianTypeRepository.saveBatch(typeEos);
        }
    }

    private void saveMusicianStyle(Long musicianId, List<Long> styleIdList) {
        final List<MusicianStyleEo> musicianStyleEos = musicianStyleRepository.listByMusicianId(musicianId);
        if (CollectionUtils.isNotEmpty(musicianStyleEos)) {
            final List<Long> addList = styleIdList.stream().filter(aLong ->
                    !musicianStyleEos.stream().map(MusicianStyleEo::getStyleId).toList().contains(aLong)).toList();
            saveMusicianStyle(addList, musicianId);
            final List<Long> delList = musicianStyleEos.stream().filter(styleEo ->
                    !styleIdList.contains(styleEo.getStyleId())).map(BaseEo::getId).toList();
            musicianStyleRepository.deleteByIds(delList);
        } else {
            saveMusicianStyle(styleIdList, musicianId);
        }
    }

    private void saveMusicianStyle(List<Long> styleIdList, Long musicianId) {
        if (CollectionUtils.isNotEmpty(styleIdList)) {
            final List<MusicianStyleEo> styleEos = styleIdList.stream().map(aLong ->
                    MusicianStyleEo.builder().styleId(aLong).musicianId(musicianId).build()).toList();
            musicianStyleRepository.saveBatch(styleEos);
        }
    }

    private void checkMusicianVo(MusicianAddVo addVo) {
        if (addVo.getStyleIdList().size() != styleRepository.countByIds(addVo.getStyleIdList())) {
            throw new ValidateException("styleId invalid");
        }

        if (addVo.getTypeIdList().size() != typeRepository.countByIds(addVo.getTypeIdList())) {
            throw new ValidateException("typeId invalid");
        }
    }

    private void checkMusicianVo(MusicianUpdateVo updateVo) {
        if (updateVo.getStyleIdList().size() != styleRepository.countByIds(updateVo.getStyleIdList())) {
            throw new ValidateException("styleId invalid");
        }

        if (updateVo.getTypeIdList().size() != typeRepository.countByIds(updateVo.getTypeIdList())) {
            throw new ValidateException("typeId invalid");
        }
    }

    private void buildVos(MusicianVo musicianVo) {
        final List<MusicianReviewEo> reviewEos = musicianReviewRepository.listByMusicianId(musicianVo.getId());

        final List<Long> typeIdList = musicianTypeRepository.listByMusicianId(musicianVo.getId())
                .stream().map(MusicianTypeEo::getTypeId).toList();

        final List<Long> styleIdList = musicianStyleRepository.listByMusicianId(musicianVo.getId())
                .stream().map(MusicianStyleEo::getStyleId).toList();

        musicianVo.setMusicianReviewVoList(SmartBeanCopy.copy(reviewEos, MusicianReviewVo.class))
                .setTypeIdList(typeIdList).setStyleIdList(styleIdList)
                .setStyleStr(buildMusicStyleStr(styleIdList))
                .setTypeStr(buildMusicTypeStr(typeIdList));
    }

    private String buildMusicTypeStr(List<Long> typeIdList) {
        if (CollectionUtils.isEmpty(typeIdList)) {
            return Constants.UNKNOWN;
        }
        final List<TypeEo> typeEos = typeRepository.listByIds(typeIdList);
        return typeEos.stream().map(TypeEo::getName).distinct().collect(Collectors.joining(" | "));
    }

    private String buildMusicStyleStr(List<Long> styleIdList) {
        if (CollectionUtils.isEmpty(styleIdList)) {
            return Constants.UNKNOWN;
        }
        final List<StyleEo> styleEos = styleRepository.listByIds(styleIdList);
        return styleEos.stream().map(StyleEo::getName).distinct().collect(Collectors.joining(" | "));
    }
}
